﻿////////////////////////////////////////////////////////////////
///
/// File: Core.cs
/// Author: Cristian Dittamo
/// Last update: 11 Dec 2009
/// Description: This file contains the definition of the 
///              main classes needed for basic/core definition of Task, Job and Scheduler.
/// To do: 
/// ------------------------------------------------------------
/// Copyright (c) 2009 Cristian Dittamo (dittamo@di.unipi.it)
/// 
/// The use and distribution terms for this software are 
/// contained in the file named license.txt, which can be found 
/// in the root of this distribution.
/// By using this software in any fashion, you are agreeing to 
/// be bound by the terms of this license.
///
/// You must not remove this notice, or any other, from this
/// software.
/// ------------------------------------------------------------
////////////////////////////////////////////////////////////////

using System;
using System.Threading;
using System.Reflection;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using PerfCounter;
using System.Diagnostics;

namespace ComputeManagement
{
    public abstract class Compute : ICloneable
    {
        public abstract Object compute(Object input);

        public Object Clone()
        {
            Object o = this.Clone();
            return o;
        }
    }

    public delegate void AsyncMethodExec();

    ///<summary>
    /// Task class - Smallest unit of computation, an asynchronous operation. 
    ///</summary>
    public class Task<Tinp,Tout> : MarshalByRefObject
    {
        ///<summary>
        /// Private fiels
        ///</summary>
        private QPerfCounter taskPerf;

        protected Object[] _args;
        protected Guid _id;
        protected volatile bool isCompleted;
        protected MethodInfo _work;
        public AutoResetEvent waitHandle;
        protected Object _worker;

        ///<summary>
        /// Default constructor
        ///</summary>
        public Task(Object worker, MethodInfo work, Guid id)
        {
            taskPerf = new QPerfCounter(MagTime.nsec);
            isCompleted = false;
            _work = work;
            _worker = worker;
            _id = id;
        }

        ///<summary>
        /// Default constructor
        ///</summary>
        public Task(Object worker, MethodInfo work, Guid id, Tinp[][] input, Tout[][] output, Object[] others)
        {
            taskPerf = new QPerfCounter(MagTime.nsec);
            isCompleted = false;
            _work = work;
            _worker = worker;
            _id = id;

            int indexAll = 0;
            int count = 0;

            for (int inIndex = 0, ouIndex = 0; inIndex < input.GetLength(0); inIndex++, ouIndex++)
            {
                Tinp[] inp = input[inIndex];
                Tout[] outp = output[ouIndex];

                if (inp != null)
                    if (outp != null)
                        if (others != null)
                            count = inp.Length + outp.Length + others.Length;
                        else
                            count = inp.Length + outp.Length;
                    else
                        count = inp.Length;
                else
                    if (outp != null)
                        if (others != null)
                            count = outp.Length + others.Length;
                        else
                            count = outp.Length;
                    else
                        throw new ArgumentNullException();

                _args = new object[count];

                if (inp != null)
                    for (int indexInArgs = 0; indexInArgs < inp.Length; indexInArgs++)
                        _args[indexAll++] = inp[indexInArgs];

                if (outp != null)
                    for (int indexOutArgs = 0; indexOutArgs < outp.Length; indexOutArgs++)
                        _args[indexAll++] = outp[indexOutArgs];

                if (others != null)
                    for (int othrIndex = 0; othrIndex < others.Length; othrIndex++)
                        _args[indexAll++] = others[othrIndex];
            }
        }

        public Guid TaskId { get { return _id; } }

        ///<summary>
        /// Get whether current task is completed or not
        ///</summary>
        public bool IsCompleted { get { return isCompleted; } }

        #region Performance evaluation
        ///<summary>
        /// Set start time for performance evaluation
        ///</summary>
        protected void StartTime() { taskPerf.Start(); }
        ///<summary>
        /// Set stop time for performance evaluation
        ///</summary>
        protected void StopTime() { taskPerf.Stop(); }
        ///<summary>
        /// Return elapsed time interval for performance evaluation
        ///</summary>
        public double ElapsedTime() { return taskPerf.Duration(1); }
        #endregion

        //public virtual void Run(List<Object[]> input, List<Object[]> output, Object[] others)
        //{
        //    isCompleted = false;
        //    try
        //    {
        //        StartTime();
        //        for ( int inIndex = 0, ouIndex = 0; inIndex < input.Count; inIndex++, ouIndex++ )
        //        {
        //            Object[] argsAll;
        //            int indexAll = 0;
        //            int count = 0;

        //            Object[] inp = input[inIndex];
        //            Object[] outp = output[ouIndex];

        //            if (inp != null)
        //                if (outp != null)
        //                    if (others != null)
        //                        count = inp.Length + outp.Length + others.Length;
        //                    else
        //                        count = inp.Length + outp.Length;
        //                else
        //                    count = inp.Length;
        //            else
        //                if (outp != null)
        //                    if (others != null)
        //                        count = outp.Length + others.Length;
        //                    else
        //                        count = outp.Length;
        //                else
        //                    throw new ArgumentNullException();

        //            argsAll = new object[count];

        //            if (inp != null)
        //                for (int indexInArgs = 0; indexInArgs < inp.Length; indexInArgs++)
        //                    argsAll[indexAll++] = inp[indexInArgs];

        //            if (outp != null)
        //                for (int indexOutArgs = 0; indexOutArgs < outp.Length; indexOutArgs++)
        //                    argsAll[indexAll++] = outp[indexOutArgs];

        //            if (others != null)
        //                for (int othrIndex = 0; othrIndex < others.Length; othrIndex++)
        //                    argsAll[indexAll++] = others[othrIndex];

        //            /// WARNING: verify the ref to output argument
        //            _work.Invoke(_worker, argsAll);
        //        }
        //    }
        //    catch (TargetException te)
        //    {
        //        throw new TargetException(te.Message);
        //    }
        //    catch (ArgumentException te)
        //    {
        //        throw new ArgumentException(te.Message);
        //    }
        //    catch (TargetInvocationException te)
        //    {
        //        throw new TargetInvocationException(te.InnerException.Message, te.InnerException);
        //    }
        //    catch (TargetParameterCountException te)
        //    {
        //        throw new TargetParameterCountException(te.Message);
        //    }
        //    catch (MethodAccessException te)
        //    {
        //        throw new MethodAccessException(te.Message);
        //    }
        //    catch (InvalidOperationException te)
        //    {
        //        throw new InvalidOperationException(te.Message);
        //    }
        //    finally
        //    {
        //        isCompleted = true;
        //        StopTime();
        //    }

        //    waitHandle.Set();
        //}

        /// <summary>
        /// Compute the method
        /// </summary>
        /// <param name="input">List of input data required by the method
        /// First dimension = which set of input data must be coinsidered
        /// Second dimension = data</param>
        /// <param name="output">List of output data produced by the method</param>
        /// <param name="others">List of other arguments required by the method</param>
        public virtual void Run()
        {
            isCompleted = false;
            try
            {
                StartTime();
                /// WARNING: verify the ref to output argument
                _work.Invoke(_worker, _args);
            }
            catch (TargetException te)
            {
                throw new TargetException(te.Message);
            }
            catch (ArgumentException te)
            {
                throw new ArgumentException(te.Message);
            }
            catch (TargetInvocationException te)
            {
                throw new TargetInvocationException(te.InnerException.Message, te.InnerException);
            }
            catch (TargetParameterCountException te)
            {
                throw new TargetParameterCountException(te.Message);
            }
            catch (MethodAccessException te)
            {
                throw new MethodAccessException(te.Message);
            }
            catch (InvalidOperationException te)
            {
                throw new InvalidOperationException(te.Message);
            }
            finally
            {
                isCompleted = true;
                StopTime();
            }

            waitHandle.Set();
        }
    }

    public class WaitHandleClass
    {
        private List<WaitHandle> _handles;
        
        public WaitHandleClass()
        {
            _handles = new List<WaitHandle>();
        }

        public void AddHandle(WaitHandle w)
        {
            _handles.Add(w);
        }

        public void Clear()
        {
            List<WaitHandle>.Enumerator lem = _handles.GetEnumerator();
            while(lem.MoveNext())
                lem.Current.Close();
            _handles.Clear();
        }

        public bool WaitAll()
        {
            return WaitHandle.WaitAll(_handles.ToArray());
        }

        public bool WaitAll(int timeout)
        {
            return WaitHandle.WaitAll(_handles.ToArray(), timeout);
        }
    }

    ///<summary>
    /// Job class - Set of tasks
    ///</summary>
    public class Job<Tinp,Tout> : MarshalByRefObject
    {
        ///<summary>
        /// Private fields
        ///</summary>
        protected delegate void AsyncTasksCaller(out Guid taskId);
        
        protected QPerfCounter _jobPerf;
        protected LinkedList<Guid> _commPartners;
        protected Dictionary<Guid, Task<Tinp,Tout>> _tasks;
        protected Dictionary<Guid, Job<Tinp, Tout>> _childJobs;
        protected Guid _jid;
        protected volatile int _iscomplete;
        protected AsyncTasksCaller _tasksCaller;
        protected WaitHandleClass _waits4tasks;
        protected Object _worker;
        protected MethodInfo _compute;

        ///<summary>
        /// Static fields -> Global Shared Memory
        ///</summary>
        public static int _cores = 1;

        ///<summary>
        /// Default constructor
        ///</summary>
        public Job(Object worker, MethodInfo compute)
        {
            _jid = Guid.NewGuid();
            _jobPerf = new QPerfCounter(MagTime.nsec);
            _cores = Environment.ProcessorCount;
            _commPartners = new LinkedList<Guid>();
            _tasks = new Dictionary<Guid, Task<Tinp,Tout>>(_cores);
            _childJobs = new Dictionary<Guid, Job<Tinp,Tout>>();
            _tasksCaller = null;
            _waits4tasks = new WaitHandleClass();
            _worker = worker;
            _compute = compute;
        }

        #region Properties
        ///<summary>
        /// Get rank = number of running tasks
        ///</summary>
        public Guid JID { get { return _jid; } }
        ///<summary>
        /// Get cores = number of available cores
        ///</summary>
        public int Cores { get { return _cores; } }
        ///<summary>
        /// Get current status
        ///</summary>
        public int IsCompleted { get { return _iscomplete; } }
        #endregion

        public bool DelTask(Guid id)
        {
            return _tasks.Remove(id);
        }

        #region Performance evaluation
        ///<summary>
        /// Set start time for performance evaluation
        ///</summary>
        protected void StartTime() { _jobPerf.Start(); }
        ///<summary>
        /// Set stop time for performance evaluation
        ///</summary>
        protected void StopTime() { _jobPerf.Stop(); }
        ///<summary>
        /// Return elapsed time interval for performance evaluation
        ///</summary>
        public double ElapsedTime() { return _jobPerf.Duration(1); }

        #endregion

        #region Communication partners management
        ///<summary>
        /// Add partner in communication
        ///</summary>
        public void AddCommPartners(Guid id)
        {
            _commPartners.AddLast(id);
        }

        ///<summary>
        /// Remove partner in communication
        ///</summary>
        public bool RemoveCommPartners(Guid jobID)
        {
            return _commPartners.Remove(jobID);
        }
        #endregion
    }

    public interface IScheduler
    {
        //static LinkedList<Job<Tinp, Tout>> jobs = new LinkedList<Job<Tinp, Tout>>();

        //static public void AddJob(Job<Tinp, Tout> job)
        //{
        //    jobs.AddLast(job);
        //}

        //static public Job<Tinp, Tout> GetNextJob()
        //{
        //    Job tmp = jobs.First.Value;
        //    jobs.RemoveFirst();
        //    return tmp;
        //}

        //static public bool DeleteJob(Job<Tinp, Tout> job)
        //{
        //    if (!jobs.Contains(job))
        //        return false;

        //    jobs.Remove(job);
        //    return true;
        //}

        void Run();
    }
}
